#include "../StdAfx.h"
#include "../DllModule.h"
#include "../ComPropVariant.h"
#include "Factory.h"
#include "SevenZipLibrary.h"

CONST LPCWSTR SevenZip::CSevenZipLibrary::LibraryName = L"7z.dll";

//Utils
STDMETHODIMP PropVarToCLSID(const PROPVARIANT & PropVar, CLSID & Result)
{
	HRESULT hr = E_INVALIDARG;

	if (PropVar.vt == VT_BSTR)
	{
		Result = *reinterpret_cast<CLSID *>(PropVar.bstrVal);
		hr = S_OK;
	}

	return hr;
}

STDMETHODIMP PropVarToString(const PROPVARIANT & PropVar, CStringW & Result)
{
	Result = ::PropVariantToStringWithDefault(PropVar, L"");
	return Result.IsEmpty() ? S_FALSE : S_OK;
}

// Ctor, Dtor, Init
SevenZip::CSevenZipLibrary::CSevenZipLibrary()
	: _Library(NULL)
{
	SetPath(NULL);
}

SevenZip::CSevenZipLibrary::~CSevenZipLibrary()
{
	SetPath(NULL);
}

STDMETHODIMP SevenZip::CSevenZipLibrary::SetPath(LPCWSTR Path)
{
	if (_Library != NULL)
	{
		::FreeLibrary(_Library);
	}

	_Legacy = FALSE;
	
	_Library = NULL;
	_LibraryPath = Path;

	_CreateObjectPtr = NULL;
	_GetMethodPropertyPtr = NULL;
	_GetHandlerPropertyPtr = NULL;
	_GetHandlerProperty2Ptr = NULL;
	_GetNumberOfFormatsPtr = NULL;
	_GetNumberOfMethodsPtr = NULL;

	return S_OK;
}

// CreateObject
STDMETHODIMP SevenZip::CSevenZipLibrary::CreateObject(REFCLSID ClassID, REFIID InterfaceID, PVOID * Result)
{
	HRESULT hr = GetProcAddress("CreateObject", _CreateObjectPtr);

	if (SUCCEEDED(hr))
	{
		hr = _CreateObjectPtr(ClassID, InterfaceID, Result);
	}

	return hr;
}


// Formats
STDMETHODIMP SevenZip::CSevenZipLibrary::GetFormatCount(PUINT32 Count)
{
	if (Count != NULL)
	{
		if (_Legacy == FALSE)
		{
			HRESULT hr = GetProcAddress("GetNumberOfFormats", _GetNumberOfFormatsPtr);

			if (SUCCEEDED(hr))
			{
				return _GetNumberOfFormatsPtr(Count);
			}

			if (hr == HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND))
			{
				_Legacy = TRUE;
			}
		}

		*Count = 1;
		return S_OK;
	}

	return E_POINTER;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::GetFormatProperty(UINT32 Format, PROPID Property, LPPROPVARIANT Result)
{
	if (Result != NULL)
	{
		if (_Legacy == FALSE)
		{
			HRESULT hr = GetHandlerProperty2(Format, Property, Result);

			if (SUCCEEDED(hr))
			{
				return hr;
			}
			
			if (hr == HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND))
			{
				_Legacy = TRUE;
			}
		}

		if (Format == 0)
		{
			return GetHandlerProperty(Property, Result);
		}

		return E_INVALIDARG;
	}

	return E_POINTER;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::GetFormatPropertyAsCLSID(UINT32 Format, PROPID Property, CLSID & Result)
{
	CComPropVariant PropVar;

	HRESULT hr = GetFormatProperty(Format, Property, &PropVar);

	if (SUCCEEDED(hr))
	{
		hr = PropVarToCLSID(PropVar, Result);
	}

	return hr;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::GetFormatPropertyAsString(UINT32 Format, PROPID Property, CStringW & Result)
{
	CComPropVariant PropVar;

	HRESULT hr = GetFormatProperty(Format, Property, &PropVar);

	if (SUCCEEDED(hr))
	{
		hr = PropVarToString(PropVar, Result);
	}

	return hr;
}

// Methods
STDMETHODIMP SevenZip::CSevenZipLibrary::GetMethodCount(PUINT32 Count)
{
	HRESULT hr = GetProcAddress("GetNumberOfMethods", _GetNumberOfMethodsPtr);

	if (SUCCEEDED(hr))
	{
		hr = _GetNumberOfMethodsPtr(Count);
	}

	return hr;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::GetMethodProperty(UINT32 Method, PROPID Property, LPPROPVARIANT Result)
{
	HRESULT hr = GetProcAddress("GetMethodProperty", _GetMethodPropertyPtr);

	if (SUCCEEDED(hr))
	{
		hr = _GetMethodPropertyPtr(Method, Property, Result);
	}

	return hr;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::GetMethodPropertyAsCLSID(UINT32 Format, PROPID Property, CLSID & Result)
{
	CComPropVariant PropVar;

	HRESULT hr = GetMethodProperty(Format, Property, &PropVar);

	if (SUCCEEDED(hr))
	{
		hr = PropVarToCLSID(PropVar, Result);
	}

	return hr;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::GetMethodPropertyAsString(UINT32 Format, PROPID Property, CStringW & Result)
{
	CComPropVariant PropVar;

	HRESULT hr = GetMethodProperty(Format, Property, &PropVar);

	if (SUCCEEDED(hr))
	{
		hr = PropVarToString(PropVar, Result);
	}

	return hr;
}

// Implementation
STDMETHODIMP SevenZip::CSevenZipLibrary::GetHandlerProperty(PROPID Property, LPPROPVARIANT Result)
{
	HRESULT hr = GetProcAddress("GetHandlerProperty", _GetHandlerPropertyPtr);

	if (SUCCEEDED(hr))
	{
		hr = _GetHandlerPropertyPtr(Property, Result);
	}

	return hr;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::GetHandlerProperty2(UINT32 Index, PROPID Property, LPPROPVARIANT Result)
{
	HRESULT hr = GetProcAddress("GetHandlerProperty2", _GetHandlerProperty2Ptr);

	if (SUCCEEDED(hr))
	{
		hr = _GetHandlerProperty2Ptr(Index, Property, Result);
	}

	return hr;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::EnsureLibraryLoaded()
{
	HRESULT hr = S_OK;

	if (_Library == NULL)
	{
		if (_LibraryPath.IsEmpty())
		{
			WCHAR Buffer[MAX_PATH];
			
			if (SUCCEEDED(hr = FindLibrary(Buffer)))
			{
				_LibraryPath = Buffer;
			}
		}

		if (SUCCEEDED(hr))
		{
			_Library = ::LoadLibraryW(_LibraryPath);

			if (_Library == NULL)
			{
				hr = HRESULT_FROM_WIN32(::GetLastError());
			}
		}
	}

	return hr;
}

STDMETHODIMP SevenZip::CSevenZipLibrary::FindLibrary(LPWSTR Path)
{
	HINSTANCE Instance = _AtlBaseModule.GetModuleInstance();
	
	if (!::GetModuleFileNameW(Instance, Path, MAX_PATH))
	{
		return HRESULT_FROM_WIN32(::GetLastError());
	}

	if (!::PathRemoveFileSpecW(Path))
	{
		return E_FAIL;
	}

	::PathCombine(Path, Path, LibraryName);

	return S_OK;
}